package com.basic.business.common.shiro;

import com.alibaba.fastjson.JSON;
import com.basic.business.common.constant.ResultMsgEnum;
import com.basic.business.common.execption.AuthException;
import com.basic.business.common.execption.BusinessException;
import com.basic.business.model.dto.SysUserDto;
import com.basic.business.model.entity.SysMenu;
import com.basic.business.model.entity.SysRole;
import com.basic.business.model.entity.SysUser;
import com.basic.business.service.LoginService;
import com.basic.business.service.SysMenuService;
import com.basic.business.service.SysRoleService;
import com.basic.business.utils.IdGenerator;
import com.basic.business.utils.RedisUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.basic.business.common.constant.ResultMsgEnum.LOGIN_PWD_ERROR;

/**
 * @author ：huage
 * @date ：Created in 2021/7/20 17:19
 * @description： 自定义Realm用于查询用户的角色和权限信息并保存到权限管理器
 */
@Slf4j
public class CustomRealm extends AbstractRealm {

    @Autowired
    private LoginService loginService;

    @Autowired
    private RedisUtil redisUtil;

    public static final String SYMBOL_UNDERLINE = "_";
    public static final String REDIS_PROJECT_PREFIX = "HUAGE_BASIC_";
    public static final String REDIS_KEY_PREFIX_LOGIN_ACCOUNT = REDIS_PROJECT_PREFIX + "LOGIN_ACCOUNT";
    public static final String REDIS_KEY_PREFIX_DICT_TYPE = REDIS_PROJECT_PREFIX + "DICT_TYPE";

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof CustomerToken;
    }

    /**
     * redis 默认过期时间 30 min
     */
    public static final int REDIS_DEFAULT_EXPRIE_TIME = 30 * 60;

    /**
     * @MethodName doGetAuthenticationInfo
     * @Description 认证配置类
     * @Param [authenticationToken]
     * @Return AuthenticationInfo
     * @Author WangShiLin
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        CustomerToken jwtToken = (CustomerToken) authenticationToken;
        Claims claims = JwtUtils.parseJWT(jwtToken.getCredentials().toString());
        if (JwtUtils.isTokenExpired(claims)) {
            throw new AuthException(ResultMsgEnum.LOGIN_EXPIRE.getCode(), "登录已过期, 请重新登录");
        }
        JwtEntity jwtObject = JSON.parseObject(claims.get(JwtUtils.JWT_KEY, String.class), JwtEntity.class);
        String username = jwtObject.getAccount();
        String password = jwtObject.getPassword();

        String jwtCache = redisUtil.get(StringUtils.joinWith(SYMBOL_UNDERLINE, REDIS_KEY_PREFIX_LOGIN_ACCOUNT, username), 0);
        log.info("[doGetAuthenticationInfo][获取缓存 jwtCache = {}, account = {}]", jwtCache, username);
        if (jwtCache == null) {
            log.warn("[doGetAuthenticationInfo][获取密码缓存失败, 查询数据库 account = {}]", username);
            SysUser user = loginService.getUserByNameAndPwd(username, password);
            JwtEntity jwtEntity = new JwtEntity(user, jwtObject.getSessionId());
            redisUtil.setex(StringUtils.joinWith(SYMBOL_UNDERLINE, REDIS_KEY_PREFIX_DICT_TYPE, username), REDIS_DEFAULT_EXPRIE_TIME, JSON.toJSONString(jwtEntity));
        } else {
            JwtEntity cacheJwtObj = JSON.parseObject(jwtCache, JwtEntity.class);
            if (!StringUtils.equals(cacheJwtObj.getSessionId(), jwtObject.getSessionId())) {
                log.error("[doGetAuthenticationInfo][当前账号在其他地方登录, account = {}]", username);
                throw new AuthException(ResultMsgEnum.LOGIN_INVALID.getCode(), "登录验证失败, 当前账号在其他地方登录, 请重新登录");
            } else if (!StringUtils.equals(password, cacheJwtObj.getPassword())) {
                log.error("[doGetAuthenticationInfo][缓存密码校验失败, account = {}, password = {}, pwdCache = {}]", username, password, cacheJwtObj.getPassword());
                throw new AuthException(LOGIN_PWD_ERROR.getCode(), "登录验证失败, 用户名或密码错误,请重新登录");
            }
        }
        return new SimpleAuthenticationInfo(jwtObject, jwtToken.getCredentials(), getName());
    }
}
